package com.zx.crowdfunding.handler;

import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradeWapPayModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeWapPayRequest;
import com.zx.crowdfunding.api.MySQLRemoteService;
import com.zx.crowdfunding.config.PayProperties;
import com.zx.crowdfunding.constant.CrowdFundingConstant;
import com.zx.crowdfunding.entity.vo.MemberLoginVO;
import com.zx.crowdfunding.entity.vo.OrderProjectVO;
import com.zx.crowdfunding.entity.vo.OrderVO;
import com.zx.crowdfunding.util.ResultEntity;

/**
 * 支付 - Handler
 * @author 郑雪
 * @date 2022-01-04
 */
@Controller
public class PayConsumerHandler {
	
	/**
	 * 日志对象
	 */
	private static final Logger LOGGER = LoggerFactory.getLogger(PayConsumerHandler.class);
	
	/**
	 * 第三方支付接口属性
	 */
	@Autowired
	private PayProperties payProperties;
	
	/**
	 * MySQL数据服务远程接口
	 */
	@Autowired
	private MySQLRemoteService mysqlRemoteService;
	
	/**
	 * 去支付，创建订单
	 * @param orderVO 订单VO对象
	 * @param session 当前会话
	 * @return 
	 */
	@RequestMapping("/create/order")
	@ResponseBody
	public String createOrder(OrderVO orderVO, HttpSession session) {
		
		// 从Session域中获取临时的OrderProjectVO对象
		OrderProjectVO orderProjectVO = (OrderProjectVO) session.getAttribute(CrowdFundingConstant.ATTR_NAME_TEMPLE_ORDER_PROJECT);
		
		// 将OrderProjectVO对象和OrderVO对象组装到一起
		orderVO.setOrderProjectVO(orderProjectVO);
		
		// 生成订单编号（当前时间加UUID）
		orderVO.setOrderNum(new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + UUID.randomUUID().toString().replace("-", ""));
		
		// 计算订单总金额
		orderVO.setOrderAmount((double) (orderProjectVO.getSupportPrice() * orderProjectVO.getReturnCount() + orderProjectVO.getFreight()));
		
		session.setAttribute("orderVO", orderVO);
		
		try {
			
			// 调用支付宝支付
			String form = sendRequestToAliPay(orderVO.getOrderNum(), orderVO.getOrderProjectVO().getProjectName(), orderVO.getOrderAmount().toString(), orderVO.getOrderProjectVO().getReturnContent() + " 【数量：" + orderVO.getOrderProjectVO().getReturnCount() + "】", null, "PROJECT_"+orderVO.getOrderProjectVO().getProjectId()+"_RETURN_"+orderVO.getOrderProjectVO().getReturnId());
			
			// 返回结果
			return form;
		
		} catch (AlipayApiException e) {
			e.printStackTrace();
			return e.getMessage();
		}
		
	}
	
	/**
	 * @Description: 支付宝异步 通知页面
	 */
	@RequestMapping("/notify")
	@ResponseBody
	public String notifyUrlMethod(HttpServletRequest request, HttpSession session) throws AlipayApiException, UnsupportedEncodingException {
		//获取支付宝POST过来反馈信息
		Map<String,String> params = new HashMap<String,String>();
		Map<String, String[]> requestParams = request.getParameterMap();
		for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) {
			String name = (String) iter.next();
			String[] values = (String[]) requestParams.get(name);
			String valueStr = "";
			for (int i = 0; i < values.length; i++) {
				valueStr = (i == values.length - 1) ? valueStr + values[i]
						: valueStr + values[i] + ",";
			}
			//乱码解决，这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
			//valueStr = new String(valueStr.getBytes("ISO-8859-1"), "gbk");
			params.put(name, valueStr);
		}
		
		//获取支付宝的通知返回参数，可参考技术文档中页面跳转同步通知参数列表(以下仅供参考)//
		//商户订单号
		String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8");
		
		//支付宝交易号
		String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8");

		//付款金额
		String total_amount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"),"UTF-8");
		
		//交易状态
		String trade_status = new String(request.getParameter("trade_status").getBytes("ISO-8859-1"),"UTF-8");

		String subject = new String(request.getParameter("subject"));
		
		String body = new String(request.getParameter("body"));
		
		//获取支付宝的通知返回参数，可参考技术文档中页面跳转同步通知参数列表(以上仅供参考)//
		//计算得出通知验证结果
		//boolean AlipaySignature.rsaCheckV1(Map<String, String> params, String publicKey, String charset, String sign_type)
		boolean verify_result = AlipaySignature.rsaCheckV1(params, payProperties.getAlipayPublicKey(), payProperties.getCharset(), payProperties.getSignType());
			
		//验证成功
		if(verify_result){
			//////////////////////////////////////////////////////////////////////////////////////////
			//请在这里加上商户的业务逻辑程序代码

			//——请根据您的业务逻辑来编写程序（以下代码仅作参考）——
			
			if(trade_status.equals("TRADE_FINISHED")){
				//判断该笔订单是否在商户网站中已经做过处理
				//如果没有做过处理，根据订单号（out_trade_no）在商户网站的订单系统中查到该笔订单的详细，并执行商户的业务程序
				//请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的
				//如果有做过处理，不执行商户的业务程序
					
				//注意：
				//如果签约的是可退款协议，退款日期超过可退款期限后（如三个月可退款），支付宝系统发送该交易状态通知
				//如果没有签约可退款协议，那么付款完成后，支付宝系统发送该交易状态通知。
			} else if (trade_status.equals("TRADE_SUCCESS")){
				//判断该笔订单是否在商户网站中已经做过处理
				//如果没有做过处理，根据订单号（out_trade_no）在商户网站的订单系统中查到该笔订单的详细，并执行商户的业务程序
				//请务必判断请求时的total_fee、seller_id与通知时获取的total_fee、seller_id为一致的
				//如果有做过处理，不执行商户的业务程序
					
				//注意：
				//如果签约的是可退款协议，那么付款完成后，支付宝系统发送该交易状态通知。
			}

			//——请根据您的业务逻辑来编写程序（以上代码仅作参考）——
			LOGGER.info("**********************支付成功（支付宝异步通知）***********************");
			LOGGER.info("* 订单号：{}", out_trade_no);
			LOGGER.info("* 支付宝交易号：{}", trade_no);
			LOGGER.info("* 付款金额：{}", total_amount);
			LOGGER.info("* 订单名称：{}", subject);
			LOGGER.info("* 商品描述：{}", body);
			LOGGER.info("* 交易状态：{}", trade_status);
			LOGGER.info("**************************************************************");

			//请不要修改或删除
			return "success";
			//////////////////////////////////////////////////////////////////////////////////////////
		}else{
			//验证失败
			LOGGER.info("支付，验签失败");
			
			//请不要修改或删除
			return "fail";
		}
	}
	
	/**
	 * @Description: 支付宝同步通知页面
	 */
	@RequestMapping("/return")
	public String returnUrlMethod(HttpServletRequest request, HttpSession session, ModelMap modelMap) throws UnsupportedEncodingException, AlipayApiException {
		//获取支付宝GET过来反馈信息
		Map<String,String> params = new HashMap<String,String>();
		Map<String, String[]> requestParams = request.getParameterMap();
		for (Iterator<String> iter = requestParams.keySet().iterator(); iter.hasNext();) {
			String name = (String) iter.next();
			String[] values = (String[]) requestParams.get(name);
			String valueStr = "";
			for (int i = 0; i < values.length; i++) {
				valueStr = (i == values.length - 1) ? valueStr + values[i]
						: valueStr + values[i] + ",";
			}
			//乱码解决，这段代码在出现乱码时使用。如果mysign和sign不相等也可以使用这段代码转化
			valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
			params.put(name, valueStr);
		}
		
		//获取支付宝的通知返回参数，可参考技术文档中页面跳转同步通知参数列表(以下仅供参考)//
		//商户订单号
		String out_trade_no = new String(request.getParameter("out_trade_no").getBytes("ISO-8859-1"),"UTF-8");

		//支付宝交易号
		String trade_no = new String(request.getParameter("trade_no").getBytes("ISO-8859-1"),"UTF-8");
		
		//付款金额
		String total_amount = new String(request.getParameter("total_amount").getBytes("ISO-8859-1"),"UTF-8");
		
		//获取支付宝的通知返回参数，可参考技术文档中页面跳转同步通知参数列表(以上仅供参考)//
		//计算得出通知验证结果
		//boolean AlipaySignature.rsaCheckV1(Map<String, String> params, String publicKey, String charset, String sign_type)
		boolean verify_result = AlipaySignature.rsaCheckV1(params, payProperties.getAlipayPublicKey(), payProperties.getCharset(), payProperties.getSignType());
		
		//验证成功
		if(verify_result){
			
			// 从Session域中获取OrderVO对象
			OrderVO orderVO = (OrderVO) session.getAttribute("orderVO");
			
			// 设置订单支付宝流水单号
			orderVO.setPayOrderNum(trade_no);
			
			// 订单名称（项目名称）
			String projectName = orderVO.getOrderProjectVO().getProjectName();
			
			// 商品描述（项目回报内容+数量）
			String projectReturn = orderVO.getOrderProjectVO().getReturnContent() + " 【数量：" + orderVO.getOrderProjectVO().getReturnCount() + "】";
			
			// 会员id
			MemberLoginVO memberLoginVO = (MemberLoginVO) session.getAttribute(CrowdFundingConstant.ATTR_NAME_LOGIN_MEMBER);
			Integer memberId = memberLoginVO.getId();
			
			LOGGER.info("**********************支付成功（支付宝同步通知）***********************");
			LOGGER.info("* 订单号：{}", out_trade_no);
			LOGGER.info("* 支付宝交易号：{}", trade_no);
			LOGGER.info("* 付款金额：{}", total_amount);
			LOGGER.info("* 订单名称：{}", projectName);
			LOGGER.info("* 商品描述：{}", projectReturn);
			LOGGER.info("**************************************************************");
			
			modelMap.addAttribute(CrowdFundingConstant.ATTR_NAME_MESSAGE , "支付成功！");
			modelMap.addAttribute("out_trade_no", out_trade_no);
			modelMap.addAttribute("trade_no", trade_no);
			modelMap.addAttribute("total_amount", total_amount);
			modelMap.addAttribute("subject", projectName);
			modelMap.addAttribute("body", projectReturn);
			
			// 调用远程接口添加订单到数据库
			ResultEntity<String> saveResultEntity = mysqlRemoteService.saveOrderVORemote(orderVO, memberId);
			
			LOGGER.info("添加订单 [{}] ", saveResultEntity.getResult());
			
			// 从Session域中移除
			session.removeAttribute("orderVO");
			session.removeAttribute(CrowdFundingConstant.ATTR_NAME_TEMPLE_ORDER_PROJECT);
			
			// 调用远程接口更新项目进度
			ResultEntity<String> editResultEntity = mysqlRemoteService.editProjectProgressRemote(orderVO.getOrderProjectVO().getProjectId());
			
			LOGGER.info("更新项目进度 [{}] ", editResultEntity.getResult());
						
			
		}else{
			
			LOGGER.info("支付，验签失败");
			
			modelMap.addAttribute(CrowdFundingConstant.ATTR_NAME_MESSAGE , "支付失败！");
		}
		
		// 跳转到支付成功页面
		return "pay-success";
	}
	
	
	/**
	 * 发送请求去支付宝支付
	 * @param outTradeNo 	 商户订单号，商户网站订单系统中唯一订单号，必填
	 * @param subject 		 订单名称，必填
	 * @param totalAmount 	 付款金额，必填
	 * @param body 			 商品描述，可空
	 * @param timeoutExpress 超时时间 可空
	 * @param productCode 	 销售产品码 必填
	 * @return 返回调用SDK生成的表单
	 * @throws AlipayApiException
	 */
	private String sendRequestToAliPay(
				String outTradeNo, 
				String subject,
				String totalAmount,
				String body, 
				String timeoutExpress, 
				String productCode
			) throws AlipayApiException {
	    
	    //调用RSA签名方式
	    AlipayClient client = new DefaultAlipayClient(
	    		payProperties.getServerUrl(), 
	    		payProperties.getAppId(), 
	    		payProperties.getPrivateKey(), 
	    		payProperties.getFormat(), 
	    		payProperties.getCharset(), 
	    		payProperties.getAlipayPublicKey(), 
	    		payProperties.getSignType());
	    AlipayTradeWapPayRequest alipay_request=new AlipayTradeWapPayRequest();
	    
	    // 封装请求支付信息
	    AlipayTradeWapPayModel model=new AlipayTradeWapPayModel();
	    model.setOutTradeNo(outTradeNo);
	    model.setSubject(subject);
	    model.setTotalAmount(totalAmount);
	    model.setBody(body);
	    model.setTimeoutExpress(timeoutExpress);
	    model.setProductCode(productCode);
	    alipay_request.setBizModel(model);
	    // 设置异步通知地址
	    alipay_request.setNotifyUrl(payProperties.getNotifyUrl());
	    // 设置同步地址
	    alipay_request.setReturnUrl(payProperties.getReturnUrl());   
	    
	    // 调用SDK生成表单
	    return client.pageExecute(alipay_request).getBody();
	}
	
}
